Skip to content

fix(lsps4): trigger channel open at peer_connected even during reestablish#9

Merged
martinsaposnic merged 1 commit intolsp-0.2.0from
fix/lsps4-peer-connected-channel-open
Mar 20, 2026
Merged

fix(lsps4): trigger channel open at peer_connected even during reestablish#9
martinsaposnic merged 1 commit intolsp-0.2.0from
fix/lsps4-peer-connected-channel-open

Conversation

@martinsaposnic
Copy link

Summary

  • When an HTLC arrives for an offline peer, htlc_intercepted stores it and sends a webhook. The peer reconnects, but peer_connected deferred all processing because channels weren't usable yet (reestablish in progress). Later, process_pending_htlcs found insufficient capacity but assumed a channel open was "already in flight" - nobody ever opened one. The HTLC would loop until expiry (45s) and fail.
  • Fix: call process_htlcs_for_peer even when channels aren't usable. calculate_htlc_actions skips non-usable channels, so it correctly returns new_channel_needed_msat and emits OpenChannel. No premature forwarding occurs since the forwards list is empty (no usable channels). Actual HTLC forwards happen via channel_ready once the new channel is established.

Test plan

  • Tested locally on regtest: payment that previously got stuck in the retry loop now opens a new zero-conf channel and forwards the HTLC successfully
  • Verify no duplicate channel opens when existing capacity IS sufficient (the existing channel just needs reestablish)
  • Verify behavior when peer has no channels at all (should be unchanged - already worked)

…blish

When an HTLC arrives for an offline peer, htlc_intercepted stores it
and sends a webhook. When the peer reconnects, peer_connected deferred
all processing if channels existed but weren't usable yet (reestablish
in progress). Later, process_pending_htlcs found insufficient capacity
but assumed a channel open was already in flight - nobody ever opened
the channel.

Fix: call process_htlcs_for_peer even when channels aren't usable.
calculate_htlc_actions skips non-usable channels, so if existing
capacity is insufficient it returns new_channel_needed_msat and
execute_htlc_actions emits OpenChannel. No premature forwarding
occurs since the forwards list is empty (no usable channels).
The actual HTLC forwards happen via channel_ready once the new
channel is established.
@martinsaposnic martinsaposnic force-pushed the fix/lsps4-peer-connected-channel-open branch from d7b97a7 to 2252def Compare March 20, 2026 13:00
@amackillop amackillop self-requested a review March 20, 2026 14:05
@martinsaposnic martinsaposnic merged commit 55f7619 into lsp-0.2.0 Mar 20, 2026
12 of 43 checks passed
martinsaposnic added a commit to moneydevkit/ldk-node that referenced this pull request Mar 20, 2026
…en fix

Updates rust-lightning rev to include moneydevkit/rust-lightning#9:
trigger OpenChannel at peer_connected even during reestablish, fixing
stuck HTLC loop when existing channel capacity is insufficient.
amackillop pushed a commit to moneydevkit/ldk-node that referenced this pull request Mar 20, 2026
…en fix (#13)

Updates rust-lightning rev to include moneydevkit/rust-lightning#9:
trigger OpenChannel at peer_connected even during reestablish, fixing
stuck HTLC loop when existing channel capacity is insufficient.
amackillop added a commit that referenced this pull request Mar 22, 2026
Channel usability (is_usable) was checked at four separate points:
htlc_intercepted, peer_connected, process_pending_htlcs, and
calculate_htlc_actions_for_peer. Each had its own deferral logic,
and they had to coordinate (the timer skipped channel opens
assuming peer_connected already handled them). This coordination
broke: PR #9 made peer_connected call process_htlcs_for_peer
during reestablish, which saw an empty capacity map because
non-usable channels were filtered out, and emitted a spurious
OpenChannel on every reconnect with a pending HTLC.

Move the usability check to execute_htlc_actions, right before
forward_intercepted_htlc. If no usable channel exists, the
forward is skipped and the HTLC stays in store for the timer to
retry. htlc_intercepted, peer_connected, and process_pending_htlcs
now all call process_htlcs_for_peer unconditionally.

calculate_htlc_actions_for_peer includes all channels in the
capacity map regardless of is_usable, so it correctly sees that a
reestablishing channel has sufficient capacity and does not request
a spurious new channel.

Change the pre-forward guard from is_peer_connected to
has_usable_channel, which covers the disconnect+reconnect race
where the peer is connected but the channel has not finished
reestablishing.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants